# The herbarium controller provides all of the actions necessary to add, modify, and show plant data.
class HerbariumController < ApplicationController
  auto_complete_for :plant, :genus
  auto_complete_for :plant, :specific_epithet
  
  # Provides the welcome screen for users. We fancy it up with some Flickr flower images.
  def index
    # Required for the login engine to return users to the appropriate page.
    store_location
  end
  
  # Provides the about page. Currently contains no actions.
  def about
    
  end

  # A list of all plants in the database. Called "browse" in the application.
  def list
    @plant_pages, @plants = paginate :plants, :per_page => 15
    @page_title = 'Listing plants.'
  end

  # Shows individual plants and their trait values.
  def show
    @plant = Plant.find(params[:id])
    # Determine the available traits through reflection
    Plant.reflect_on_all_associations.each do | assoc |
      trait = assoc.name.to_s
      # We don't want the :belongs_to traits
      unless assoc.macro == :belongs_to
        # If the plant is new, we create each trait to avoid nil errors in the view.
        if eval("@plant.#{trait}").nil?
          eval("@plant.#{trait}= #{trait.camelize}.create")
        end
      end
      # Make each plant trait available by name.
      eval("@#{trait}= @plant#{trait}")
    end
    @page_title = 'Plant details.'
    store_location
    # If the user tries to access a plant that doesn't exist, redirect them to the index.
    rescue
      logger.error("Attempt to access invalid plant #{params[:id]}")
      redirect_to_index('That plant does not exist!')
  end

  # Creates a new plant record.
  # We query the request to collapse the new and create methods into a single new method.
  def create_plant
    @families = Family.find(:all, :order => 'apg_name')
    @plant = Plant.new(params[:plant])
    @plant.created_by = current_user.id
    @page_title = 'Create new plant.'
    # Attempt to save the new plant.
    if request.post? and @plant.save
      # If successful, update the user contributions and redirect to the plant's detail view.
      increment_user_contributions
      #--
      # TODO: count_text = pluralize(current_user.plants_added, "plant")
      redirect_to :action => 'show', :id => @plant
      flash[:notice] = "#{current_user.firstname}, the plant was created. Now add some data!"
    else
      render :action => 'create_plant'
      flash[:notice] = 'Plant was not created.'
    end
  end

  # Edit a plant's taxonomic details (genus, specific_epithet, and family).
  def edit_plant
    @plant = Plant.find(params[:id])
    @families = Family.find(:all, :order => 'apg_name')
    @page_title = 'Edit plant taxonomy.'
  end

  # Update changes made to a plant's traits.
  def update
    @plant = Plant.find(params[:id])
    # Detect the plant traits through reflection.
    Plant.reflect_on_all_associations.each do | assoc |
      trait = assoc.name
      # Update the :has_one traits.
      if assoc.macro == :has_one
        eval("#{trait.to_s.camelize}.update(params[:#{trait}].keys,
        params[:#{trait}].values) if params[:#{trait}]")
        validate_uri(trait)
      # Update the :has_many traits. The trait name has to be singular.
      elsif assoc.macro == :has_many
        trait = trait.to_s.singularize
        eval("#{trait.to_s.camelize}.update(params[:#{trait}].keys,
        params[:#{trait}].values) if params[:#{trait}]")
        #--
        # FIXME: This validation doesn't work for :has_many traits.
        # validate_uri(trait.pluralize)
      end
    end
    if @plant.update_attributes(params[:plant])
      increment_user_contributions
      if flash[:warning].nil?
        flash[:notice] = 'Plant was successfully updated.'
      end
      redirect_to :action => 'show', :id => @plant
    else
      flash[:warning] = 'Plant was not updated!'
    end
  end
  
  # Removes a plant from the database.
  # Associations are :dependent => true, so traits should also be removed.
  # This is an action in response to an AJAX call.
  def destroy_plant
    Plant.find(params[:id]).destroy
    render(:text => '')
  end
  
  # Update an individual trait.
  # This is an action in response to an AJAX call.
  def update_trait
    trait = params[:trait]
    @plant = Plant.find(params[:plant])
    eval("@#{trait} = @plant.#{trait}")
    render(:layout => false)
  end
  
  # Add another entry for traits with multiple values (:has_many traits).
  # This is an action in response to an AJAX call.
  def add_item
    @plant = Plant.find(params[:plant])
    @trait = params[:trait]
    # Only one eval() per statement? Requires the creation of the temporary new_item.
    new_item = eval("#{@trait.camelize}.new(:content => 0, :error => 0, :comment => 'comment', :connotea_uri => 'empty')")
    if eval("@plant.#{@trait.pluralize}.push(new_item)")
      render(:partial => 'trait_list')
    else
      flash[:warning] = 'The item connotea_uri could not be added!'
      redirect_to :action => 'show', :id => @plant      
    end
  end

  # Update an entry for traits with multiple values (:has_many traits).
  # This is an action in response to an AJAX call.
  def update_item
    trait = params[:trait]
    item = eval(trait.camelize).find(params[:id])
    eval("@#{trait}=item")
    @plant = Plant.find(params[:plant])
    render(:layout => false)
  end

  # Remove an entry for traits with multiple values (:has_many traits).
  # This is an action in response to an AJAX call.  
  def destroy_item
    model = params[:trait]
    item = eval(model).find(params[:id])
    @plant = Plant.find(item.plant_id)
    if eval(model).find(params[:id]).destroy
      @trait = model.underscore
      render(:partial => 'trait_list')
    else
      flash[:warning] = 'The item could not be deleted!'
      redirect_to :action => 'show', :id => @plant
    end
  end
  
  # Search plant taxonomy for matching querries.
  def search
    @query = params[:query]
    @plants = Plant.search "#{@query}", :include => [:family]
    @page_title = "Search results for #{@query}."
  end
  
  protected
  
  # Ensure that content fields have associated references.
  def validate_uri(trait)
    eval("@trait = @plant.#{trait}")
    flash[:warning] = 'A reference is required!' if @trait.connotea_uri.blank? unless @trait.content.blank?
  end
  
  # Is the user logged in?
  def user?
    !session[:user].nil?
  end
  
  # Set the notice if a parameter is given, then redirect back
  # to the current controller's +index+ action
  # Stolen from Agile Web Development with Rails
  def redirect_to_index(msg = nil)         #:doc:
    flash[:notice] = msg if msg
    redirect_to(:action => 'index')
  end
  
  # Update the user's contribution score.
  def increment_user_contributions
    if params["action"] == 'new'
      current_user.plants_added += 1
    elsif params["action"] == 'update'
      current_user.plants_modified += 1
    end
    current_user.save
  end
  
  # Provides the appropriate pluralization of nouns.
  # Stolen from Agile Web Development with Rails
  def pluralize(count, noun)
    case count
    when 0: "no #{noun.pluralize}"
    when 1: "one #{noun}"
    else    "#{count} #{noun.pluralize}"
    end
  end
  
end